# Dependencies
#!pip install plotly
#!pip install xlrd
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
from sklearn.decomposition import PCA
exp = pd.read_excel('données_brutes_cpam_vf.xlsx') # Expenses
exp.head() # No NaNs
| Département | Année | Omnipraticiens libéraux | Spécialistes libéraux | Dentistes libéraux | Infirmiers libéraux | Masseurs kinésithérapeutes libéraux | Laboratoires | LPP | Frais de déplacement des malades | Prestations en espèces | Total soins de ville | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2010 | 3.582113e+07 | 6.444343e+07 | 21467728.07 | 27085715.55 | 17216247.62 | 2.123551e+07 | 28271592.30 | 2.452481e+07 | 1.008467e+08 | 3.409128e+08 |
| 1 | 2 | 2010 | 4.478957e+07 | 6.449599e+07 | 20494481.65 | 29021929.08 | 14727914.21 | 2.254906e+07 | 38204694.09 | 3.154759e+07 | 6.901815e+07 | 3.348494e+08 |
| 2 | 3 | 2010 | 2.551692e+07 | 3.789959e+07 | 13197445.20 | 20639792.20 | 13171912.57 | 1.435779e+07 | 21777766.98 | 1.883787e+07 | 4.327667e+07 | 2.086758e+08 |
| 3 | 4 | 2010 | 1.374312e+07 | 2.117643e+07 | 5844021.90 | 15259847.45 | 8078144.88 | 8.486306e+06 | 11472255.18 | 1.434790e+07 | 1.978911e+07 | 1.181971e+08 |
| 4 | 5 | 2010 | 1.078393e+07 | 1.320859e+07 | 4811436.19 | 9458520.60 | 7210305.26 | 5.575621e+06 | 8548619.79 | 9.310945e+06 | 1.707412e+07 | 8.598209e+07 |
exp_gr = exp.groupby(['Année']).agg('sum').reset_index() # Sum per year
year = exp_gr['Année']
exp_gr_perc = exp_gr.pct_change().dropna()
exp_gr_perc['Année'] = year
exp_gr_perc # percentage increase year on year
| Année | Omnipraticiens libéraux | Spécialistes libéraux | Dentistes libéraux | Infirmiers libéraux | Masseurs kinésithérapeutes libéraux | Laboratoires | LPP | Frais de déplacement des malades | Prestations en espèces | Total soins de ville | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2011 | 0.054165 | 0.022613 | 0.008391 | 0.060670 | 0.023866 | 0.017908 | 0.044253 | 0.035526 | 0.025794 | 0.032526 |
| 2 | 2012 | -0.007480 | 0.005810 | 0.004859 | 0.073691 | 0.047216 | -0.017062 | 0.040825 | 0.042050 | -0.015217 | 0.012959 |
| 3 | 2013 | 0.062142 | 0.022010 | 0.005229 | 0.083629 | 0.064528 | 0.008333 | 0.065945 | 0.056759 | 0.003087 | 0.036891 |
| 4 | 2014 | 0.024483 | 0.026739 | 0.006234 | 0.054909 | 0.044684 | -0.005254 | 0.057010 | 0.033408 | 0.040336 | 0.033629 |
| 5 | 2015 | 0.028330 | 0.041878 | 0.034892 | 0.059087 | 0.036142 | 0.000893 | 0.060857 | 0.038878 | 0.035321 | 0.039126 |
| 6 | 2016 | 0.014915 | 0.034816 | 0.015499 | 0.046523 | 0.037027 | 0.023789 | 0.056922 | 0.046614 | 0.040696 | 0.036436 |
| 7 | 2017 | 0.027195 | 0.025717 | 0.013077 | 0.042732 | 0.029900 | 0.003413 | 0.045729 | 0.037118 | 0.039769 | 0.031895 |
| 8 | 2018 | 0.001574 | 0.042035 | 0.010243 | 0.051368 | 0.034026 | -0.001549 | 0.038450 | 0.033860 | 0.052274 | 0.034439 |
| 9 | 2019 | 0.094050 | 0.095079 | 0.108328 | 0.100923 | 0.084022 | 0.086796 | 0.111242 | 0.041637 | 0.070031 | 0.087762 |
# Some stats
# Perc
soins_de_villes_perc = exp_gr_perc['Total soins de ville'].mean() # 3.8 %
# Abs values
tot_soins = pd.DataFrame(exp_gr.sum(), columns=['total de 2010 a 2019'])
tot_soins['poids'] = 100*round(tot_soins['total de 2010 a 2019'] / tot_soins.loc['Total soins de ville','total de 2010 a 2019'],2)
tot_soins = tot_soins.sort_values('poids', ascending=False)
tot_soins
| total de 2010 a 2019 | poids | |
|---|---|---|
| Total soins de ville | 4.678051e+11 | 100.0 |
| Prestations en espèces | 9.628912e+10 | 21.0 |
| Spécialistes libéraux | 9.393026e+10 | 20.0 |
| Omnipraticiens libéraux | 5.704222e+10 | 12.0 |
| Infirmiers libéraux | 4.966443e+10 | 11.0 |
| LPP | 4.997623e+10 | 11.0 |
| Masseurs kinésithérapeutes libéraux | 3.044648e+10 | 7.0 |
| Frais de déplacement des malades | 3.419082e+10 | 7.0 |
| Dentistes libéraux | 2.728325e+10 | 6.0 |
| Laboratoires | 2.898227e+10 | 6.0 |
| Année | 2.014500e+04 | 0.0 |
fig = px.line(
exp_gr,
x='Année',
y=sorted([col for col in list(exp_gr.columns) if col not in ['Année', 'Total soins de ville']]),
width=1000, height=700,
markers=True,
title = "Dépense des soins de ville entre 2010 et 2019 en France - En Milliards d '€",
template="simple_white",
labels={
'variable':'poste de dépense',
'value':'Dépense'
}
)
fig.update_yaxes( # the y-axis is in euros
tickprefix="€", showgrid=True
)
fig.add_shape( # add a vartical "target" line
type="line", line_color="salmon", line_width=3, opacity=1, line_dash="dot",
x0=2018, x1=2018, xref="x", y0=0, y1=12e9, yref="y"
)
fig.add_annotation( # Text
text="accélération de la hausse", x=2018, y=8e9, arrowhead=1, showarrow=False
)
fig.show()
exp_gr_pivot = exp_gr.melt( # Pivot
id_vars='Année',
var_name='Composante',
value_name="Dépense"
)
fig = px.bar(
exp_gr_pivot[(exp_gr_pivot['Année']==2019) & (exp_gr_pivot['Composante']!='Total soins de ville')] \
.sort_values('Dépense', ascending=False),
x='Composante',
y='Dépense',
width=1000, height=700,
title = "Dépenses en soins de ville pour l'année 2019 - En Milliards d '€",
template="simple_white"
)
fig.update_yaxes( # the y-axis is in euros
tickprefix="€", showgrid=True
)
Commentaire:
France - Par An Les soins de ville voient leur valeur augmenter constamment entre 2010 et 2019 passant de 40.3 a 56.6 milliards d'euros de depenses (augmentation de +40%). Si l'evolution est plutot constante d'annee en annee, aux alentours de +3%, on remarque une forte hausse entre 2018 et 2019 (+9%). Quelles sont les causes/composantes principales expliquant une telle hausse? Tout d'abord, les soins de villes, dans leur entierete entre 2010 et 2019, ont represente une depense considerable de 467 milliards d'euros: celle ci-est majoritairement due aux Prestations en espèces et Spécialistes libéraux qui representent a eux seuls 190 milliards d'euros, i.e. 41% de la depense totale. Suivent ensuite les Omnipraticiens libéraux, Infirmiers libéraux et LPP, chacun contribuant pour ~10% de la depense totale. On constate enfin que tous les postes de dépense sont en hausse constante d'annee en annee.
En termes de comportement des depenses on observe trois clusters principaux: un premier groupe incluant les Prestations en espèces et Spécialistes libéraux comme souligne ci-dessus. Ce groupe est le vecteur principal des depenses avec une augmentation assez prononcee dans la periode d'interet. Le deuxieme groupe inclut les Omnipraticiens libéraux, Infirmiers libéraux et LPP avec une correlation des depenses tres importante des LPP et Infirmiers libéraux, leur depenses etant quasi juxtaposees. Enfin, le troisieme groupe inclut les Frais de déplacement des malades, Masseurs kinésithérapeutes libéraux, Laboratoires et Dentistes libéraux. Ce dernier, affiche en 2019 des depenses entre 2 et 3 milliards d'euros et a une hausse legerement plus attenuee par rapport aux deux premiers clusters
exp.head()
| Département | Année | Omnipraticiens libéraux | Spécialistes libéraux | Dentistes libéraux | Infirmiers libéraux | Masseurs kinésithérapeutes libéraux | Laboratoires | LPP | Frais de déplacement des malades | Prestations en espèces | Total soins de ville | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2010 | 3.582113e+07 | 6.444343e+07 | 21467728.07 | 27085715.55 | 17216247.62 | 2.123551e+07 | 28271592.30 | 2.452481e+07 | 1.008467e+08 | 3.409128e+08 |
| 1 | 2 | 2010 | 4.478957e+07 | 6.449599e+07 | 20494481.65 | 29021929.08 | 14727914.21 | 2.254906e+07 | 38204694.09 | 3.154759e+07 | 6.901815e+07 | 3.348494e+08 |
| 2 | 3 | 2010 | 2.551692e+07 | 3.789959e+07 | 13197445.20 | 20639792.20 | 13171912.57 | 1.435779e+07 | 21777766.98 | 1.883787e+07 | 4.327667e+07 | 2.086758e+08 |
| 3 | 4 | 2010 | 1.374312e+07 | 2.117643e+07 | 5844021.90 | 15259847.45 | 8078144.88 | 8.486306e+06 | 11472255.18 | 1.434790e+07 | 1.978911e+07 | 1.181971e+08 |
| 4 | 5 | 2010 | 1.078393e+07 | 1.320859e+07 | 4811436.19 | 9458520.60 | 7210305.26 | 5.575621e+06 | 8548619.79 | 9.310945e+06 | 1.707412e+07 | 8.598209e+07 |
exp_pivot = exp.melt( # Pivot
id_vars=['Département','Année'],
var_name='Composante',
value_name="Dépense"
)
exp_pivot_top3_tot = exp_pivot[exp_pivot.groupby('Année')['Dépense'].rank(ascending=False).le(3)]
exp_pivot_top3_tot['Département'] = exp_pivot_top3_tot['Département'].astype('str').replace(to_replace={
'13':'Bouches-du-Rhône',
'59':'Nord',
'69':'Rhône',
'75':'Paris'
})
exp_pivot_top3_tot
C:\Users\nicco\anaconda3\envs\test-env\lib\site-packages\ipykernel_launcher.py:11: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
| Département | Année | Composante | Dépense | |
|---|---|---|---|---|
| 8652 | Bouches-du-Rhône | 2010 | Total soins de ville | 1.997630e+09 |
| 8697 | Nord | 2010 | Total soins de ville | 1.792959e+09 |
| 8713 | Paris | 2010 | Total soins de ville | 1.283393e+09 |
| 8748 | Bouches-du-Rhône | 2011 | Total soins de ville | 2.045580e+09 |
| 8793 | Nord | 2011 | Total soins de ville | 1.873293e+09 |
| 8809 | Paris | 2011 | Total soins de ville | 1.288387e+09 |
| 8844 | Bouches-du-Rhône | 2012 | Total soins de ville | 2.060623e+09 |
| 8889 | Nord | 2012 | Total soins de ville | 1.914837e+09 |
| 8905 | Paris | 2012 | Total soins de ville | 1.295414e+09 |
| 8940 | Bouches-du-Rhône | 2013 | Total soins de ville | 2.151754e+09 |
| 8985 | Nord | 2013 | Total soins de ville | 1.986267e+09 |
| 9001 | Paris | 2013 | Total soins de ville | 1.314424e+09 |
| 9036 | Bouches-du-Rhône | 2014 | Total soins de ville | 2.205873e+09 |
| 9081 | Nord | 2014 | Total soins de ville | 2.076888e+09 |
| 9097 | Paris | 2014 | Total soins de ville | 1.352763e+09 |
| 9132 | Bouches-du-Rhône | 2015 | Total soins de ville | 2.287674e+09 |
| 9177 | Nord | 2015 | Total soins de ville | 2.157864e+09 |
| 9193 | Paris | 2015 | Total soins de ville | 1.370263e+09 |
| 9228 | Bouches-du-Rhône | 2016 | Total soins de ville | 2.351559e+09 |
| 9273 | Nord | 2016 | Total soins de ville | 2.232632e+09 |
| 9289 | Paris | 2016 | Total soins de ville | 1.417495e+09 |
| 9324 | Bouches-du-Rhône | 2017 | Total soins de ville | 2.400885e+09 |
| 9369 | Nord | 2017 | Total soins de ville | 2.296586e+09 |
| 9385 | Paris | 2017 | Total soins de ville | 1.454464e+09 |
| 9420 | Bouches-du-Rhône | 2018 | Total soins de ville | 2.468346e+09 |
| 9465 | Nord | 2018 | Total soins de ville | 2.378330e+09 |
| 9475 | Rhône | 2018 | Total soins de ville | 1.507025e+09 |
| 9516 | Bouches-du-Rhône | 2019 | Total soins de ville | 2.650593e+09 |
| 9561 | Nord | 2019 | Total soins de ville | 2.539484e+09 |
| 9577 | Paris | 2019 | Total soins de ville | 1.628976e+09 |
fig = px.scatter(
exp[['Année','Total soins de ville', 'Département']],
x='Année',
y='Total soins de ville',
template="simple_white",
hover_data = ['Département'],
title = "Dépenses en soins de ville par departement entre 2010 et 2019 - En Milliards d '€"
)
fig.update_yaxes( # the y-axis is in euros
tickprefix="€", showgrid=True
)
exp_pivot_tail3_tot = exp_pivot[exp_pivot.Composante=='Total soins de ville']
exp_pivot_tail3_tot = exp_pivot_tail3_tot[exp_pivot_tail3_tot.groupby('Année')['Dépense'].rank(ascending=True).le(1)] # Min values
exp_pivot_tail3_tot['Dépense - Moyenne mobile sur 3 ans'] = exp_pivot_tail3_tot['Dépense'].rolling(3).mean()
exp_pivot_tail3_tot
| Département | Année | Composante | Dépense | Dépense - Moyenne mobile sur 3 ans | |
|---|---|---|---|---|---|
| 8686 | 48 | 2010 | Total soins de ville | 4.121377e+07 | NaN |
| 8782 | 48 | 2011 | Total soins de ville | 4.265398e+07 | NaN |
| 8878 | 48 | 2012 | Total soins de ville | 4.312395e+07 | 4.233057e+07 |
| 8974 | 48 | 2013 | Total soins de ville | 4.458788e+07 | 4.345527e+07 |
| 9070 | 48 | 2014 | Total soins de ville | 4.649024e+07 | 4.473402e+07 |
| 9166 | 48 | 2015 | Total soins de ville | 4.805181e+07 | 4.637664e+07 |
| 9262 | 48 | 2016 | Total soins de ville | 4.924266e+07 | 4.792824e+07 |
| 9358 | 48 | 2017 | Total soins de ville | 4.958974e+07 | 4.896140e+07 |
| 9454 | 48 | 2018 | Total soins de ville | 5.055527e+07 | 4.979589e+07 |
| 9550 | 48 | 2019 | Total soins de ville | 5.781228e+07 | 5.265243e+07 |
exp_pivot_sub = exp_pivot[exp_pivot.Composante != 'Total soins de ville']
exp_pivot_sub
exp_pivot_sub_gr = exp_pivot_sub[exp_pivot_sub.groupby(['Année','Département'])['Dépense'] \
.rank(ascending=False) \
.le(1)]\
.sort_values(['Année','Dépense'], ascending=[True,False]) \
.groupby(['Année'])['Composante'] \
.value_counts() \
.to_frame(name='fréquence') \
.reset_index()
exp_pivot_sub_gr['pourcentage'] = 100 * exp_pivot_sub_gr['fréquence'] / 96 # 96: number of departements
exp_pivot_sub_gr
| Année | Composante | fréquence | pourcentage | |
|---|---|---|---|---|
| 0 | 2010 | Prestations en espèces | 59 | 61.458333 |
| 1 | 2010 | Spécialistes libéraux | 35 | 36.458333 |
| 2 | 2010 | Frais de déplacement des malades | 1 | 1.041667 |
| 3 | 2010 | Infirmiers libéraux | 1 | 1.041667 |
| 4 | 2011 | Prestations en espèces | 58 | 60.416667 |
| 5 | 2011 | Spécialistes libéraux | 35 | 36.458333 |
| 6 | 2011 | Infirmiers libéraux | 2 | 2.083333 |
| 7 | 2011 | Frais de déplacement des malades | 1 | 1.041667 |
| 8 | 2012 | Prestations en espèces | 57 | 59.375000 |
| 9 | 2012 | Spécialistes libéraux | 36 | 37.500000 |
| 10 | 2012 | Infirmiers libéraux | 2 | 2.083333 |
| 11 | 2012 | Frais de déplacement des malades | 1 | 1.041667 |
| 12 | 2013 | Prestations en espèces | 56 | 58.333333 |
| 13 | 2013 | Spécialistes libéraux | 36 | 37.500000 |
| 14 | 2013 | Infirmiers libéraux | 3 | 3.125000 |
| 15 | 2013 | Frais de déplacement des malades | 1 | 1.041667 |
| 16 | 2014 | Prestations en espèces | 59 | 61.458333 |
| 17 | 2014 | Spécialistes libéraux | 33 | 34.375000 |
| 18 | 2014 | Infirmiers libéraux | 3 | 3.125000 |
| 19 | 2014 | Frais de déplacement des malades | 1 | 1.041667 |
| 20 | 2015 | Prestations en espèces | 57 | 59.375000 |
| 21 | 2015 | Spécialistes libéraux | 35 | 36.458333 |
| 22 | 2015 | Infirmiers libéraux | 3 | 3.125000 |
| 23 | 2015 | Frais de déplacement des malades | 1 | 1.041667 |
| 24 | 2016 | Prestations en espèces | 59 | 61.458333 |
| 25 | 2016 | Spécialistes libéraux | 33 | 34.375000 |
| 26 | 2016 | Infirmiers libéraux | 3 | 3.125000 |
| 27 | 2016 | Frais de déplacement des malades | 1 | 1.041667 |
| 28 | 2017 | Prestations en espèces | 58 | 60.416667 |
| 29 | 2017 | Spécialistes libéraux | 33 | 34.375000 |
| 30 | 2017 | Infirmiers libéraux | 4 | 4.166667 |
| 31 | 2017 | Frais de déplacement des malades | 1 | 1.041667 |
| 32 | 2018 | Prestations en espèces | 60 | 62.500000 |
| 33 | 2018 | Spécialistes libéraux | 30 | 31.250000 |
| 34 | 2018 | Infirmiers libéraux | 4 | 4.166667 |
| 35 | 2018 | Frais de déplacement des malades | 2 | 2.083333 |
| 36 | 2019 | Prestations en espèces | 60 | 62.500000 |
| 37 | 2019 | Spécialistes libéraux | 31 | 32.291667 |
| 38 | 2019 | Infirmiers libéraux | 4 | 4.166667 |
| 39 | 2019 | Frais de déplacement des malades | 1 | 1.041667 |
fig = px.bar(
exp_pivot_top3_tot,
x='Année',
y='Dépense',
color= 'Département',
width=1000, height=700,
title = "Top-3 des Départements par dépense en soins de ville - de 2010 à 2019 en Milliards d '€",
barmode='group',
template="simple_white"
)
fig.update_yaxes( # the y-axis is in dollars
tickprefix="€", showgrid=True
)
fig.update_layout( # customize font and legend orientation & position
#font_family="Rockwell",
legend=dict(
title=None, orientation="h", y=1, yanchor="bottom", x=0.5, xanchor="center"
)
)
fig.show()
fig = px.bar(
exp_pivot_sub_gr,
x='Année',
y='pourcentage',
color= 'Composante',
width=1000, height=700,
title = "Distribution de la première dépense de soins par département",
#barmode='group',
template="simple_white"
)
fig.update_yaxes( # the y-axis is in dollars
ticksuffix="%",showgrid=True
)
fig.update_layout( # customize font and legend orientation & position
#font_family="Rockwell",
legend=dict(
title=None, orientation="h", y=1, yanchor="bottom", x=0.5, xanchor="center"
)
)
fig.show()
Commentaire:
Par Departement - Par An En analysant plus en detail les depenses de soins de ville par departement on remarque que deux departements se distinguent: les Bouches-du-Rhône avec des depenses constamment en hausse et atteignant les 2.6 milliards d'euros en 2019 et le departement du Nord avec une croissance similaire et 2.5 milliards d'euros en 2019. Suit ensuite Paris, chaque annee, a la troisieme place avec une depense bien inferieur (60% seulement par rapport aux Bouches-du-Rhône en 2019) et le Rhône qui apparait exceptionnellement dans le top-3 en 2018.
En s'interessant plus specifiquement aux depenses de soins majoritaires par departement (cf FIG) on retrouve les Prestations en espèces en tant que premiere depense de soins pour environ 60 des 96 departements de France chaque annee, le chiffre etant tres stable entre 2010 et 2019. On trouve ensuite les depenses liees aux Spécialistes libéraux comme etant la premiere depense pour environ 34 departements avec une legere baisse en 2019. Moins de 5% des departement ont une depense primaire autre que les Prestations en espèces et les Spécialistes libéraux (les Frais de déplacement des malades et Infirmiers libéraux).
De l'autre cote du spectre, on observe chaque annee des departements avec des depenses bien plus basses avec des valeurs proches de quelques dizaines de millions d'euros seuelement. Cependant, meme lorsqu'on s'interesse aux depenses minimales, on retrouve tout de meme une hausse des depenses, par example en Lozere, avec une moyenne mobile de ses depenses, sur les trois dernieres annees, ayant augmente de 24% entre 2012 et 2019 (58 millions d'euros en 2019).
En conclusion, on constate une hausse significative des soins de ville entre 2010 et 2019 avec les Prestations en espèces et Spécialistes libéraux etant les depenses plus importantes chaque annee. Au niveaux des differents departements, les Bouches-du-Rhône et le Nord engendrent plus de depenses et la Lozere clot le classement avec une depense 4x fois inferieure par rapport aux Bouches-du-Rhône. Cela demontre une hétérogénéité de la demande en soins de ville entre les départements qui peut potentiellement s'expliquer en prenant en compte des donnees supplementaires telles que les estimations de population par département, sexe et tranche d’âge ainsi que le nombre d’ALP (affections de longue durée) par département et par type d’ALD.
exp19 = exp[exp.Année==2019]
exp19['Département'] = exp19['Département'].astype('str')
px.violin(
exp19,
y="Total soins de ville",
width=1000, height=700,
template="simple_white",
title = 'Hétérogénéité des dépenses en soins de ville entre les départements en 2019',
box=True
)
C:\Users\nicco\anaconda3\envs\test-env\lib\site-packages\ipykernel_launcher.py:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
# Population estimate
pop = pd.read_excel(
'estim-pop-dep-sexe-gca-1975-2021.xlsx',
sheet_name = '2019',
header = [3,4]
)
pop.columns = pop.columns.map('_'.join).str.strip('_')
pop = pop.rename(columns={
'Départements_Unnamed: 0_level_1':'Département',
'Départements_Unnamed: 1_level_1':'Nom du département',
})
pop = pop.dropna(subset=['Département'])
pop = pop[:-1] # Source : Insee - Estimations de population (résultats provisoires arrêtés fin 2020)
pop_fm = pop.loc[96,:]
pop_dom = pop[pop.Département == 'DOM']
pop_fm_dom = pop[pop.Département == 'France métropolitaine et DOM']
pop = pop[~pop.Département.isin([
'France métropolitaine ', 'DOM', 'France métropolitaine et DOM'
])]
pop.head()
| Département | Nom du département | Ensemble_0 à 19 ans | Ensemble_20 à 39 ans | Ensemble_40 à 59 ans | Ensemble_60 à 74 ans | Ensemble_75 ans et plus | Ensemble_Total | Hommes_0 à 19 ans | Hommes_20 à 39 ans | Hommes_40 à 59 ans | Hommes_60 à 74 ans | Hommes_75 ans et plus | Hommes_Total | Femmes_0 à 19 ans | Femmes_20 à 39 ans | Femmes_40 à 59 ans | Femmes_60 à 74 ans | Femmes_75 ans et plus | Femmes_Total | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 01 | Ain | 168091.0 | 151167.0 | 178448.0 | 102042.0 | 53112.0 | 652860.0 | 86874.0 | 75432.0 | 89041.0 | 49114.0 | 21362.0 | 321823.0 | 81217.0 | 75735.0 | 89407.0 | 52928.0 | 31750.0 | 331037.0 |
| 1 | 02 | Aisne | 132910.0 | 115437.0 | 137721.0 | 96234.0 | 48683.0 | 530985.0 | 68421.0 | 57832.0 | 68188.0 | 46110.0 | 18188.0 | 258739.0 | 64489.0 | 57605.0 | 69533.0 | 50124.0 | 30495.0 | 272246.0 |
| 2 | 03 | Allier | 68963.0 | 62009.0 | 87142.0 | 71555.0 | 45868.0 | 335537.0 | 35605.0 | 31302.0 | 42841.0 | 33500.0 | 17570.0 | 160818.0 | 33358.0 | 30707.0 | 44301.0 | 38055.0 | 28298.0 | 174719.0 |
| 3 | 04 | Alpes-de-Haute-Provence | 34398.0 | 30454.0 | 43971.0 | 34900.0 | 20940.0 | 164663.0 | 18068.0 | 15171.0 | 21352.0 | 16909.0 | 8522.0 | 80022.0 | 16330.0 | 15283.0 | 22619.0 | 17991.0 | 12418.0 | 84641.0 |
| 4 | 05 | Hautes-Alpes | 30038.0 | 27524.0 | 38137.0 | 28913.0 | 15945.0 | 140557.0 | 15639.0 | 13819.0 | 18804.0 | 13947.0 | 6382.0 | 68591.0 | 14399.0 | 13705.0 | 19333.0 | 14966.0 | 9563.0 | 71966.0 |
# ALD
ald = pd.read_excel('ALD-prevalentes-par-departement_serie-annuelle_vf.xls')
ald = ald.rename(columns={
'Code département':'Département',
'Total':'Total ALD',
})
ald = ald[:-3] # Pour Mayotte...
ald.head()
| Département | Nom du département | Total ALD | Insuffisance cardiaque grave, troubles du rythme graves, cardiopathies valvulaires graves, cardiopathies congénitales graves (ALD5) | Diabète de type 1 et diabète de type 2 (ALD8) | Maladie coronaire (ALD13) | Affections psychiatriques de longue durée (ALD23) | Tumeur maligne, affection maligne du tissu lymphatique ou hématopoïétique (ALD30) | |
|---|---|---|---|---|---|---|---|---|
| 0 | 01 | Ain | 20643.0 | 1963.0 | 4565.0 | 1994.0 | 2122.0 | 3873 |
| 1 | 02 | Aisne | 23514.0 | 2199.0 | 6284.0 | 2417.0 | 2160.0 | 3718 |
| 2 | 03 | Allier | 25140.0 | 2151.0 | 5169.0 | 2370.0 | 3503.0 | 4353 |
| 3 | 04 | Alpes-de-Haute-Provence | 23538.0 | 2065.0 | 4135.0 | 2143.0 | 3540.0 | 3887 |
| 4 | 05 | Hautes-Alpes | 23613.0 | 2367.0 | 3490.0 | 1941.0 | 3462.0 | 3945 |
pop['Département'] = pop['Département'].astype('str').str.strip()
ald['Département'] = ald['Département'].astype('str').str.strip()
df = ald.merge(pop.drop('Nom du département',axis=1), how='left', on='Département')# No NaNs
for col in df.columns[2:]:
df[col] = df[col].astype('int')
# Add expenses as well
exp19['Département'] = exp19['Département'].replace({
'1':'01','2':'02','3':'03','4':'04',
'5':'05','6':'06','7':'07','8':'08',
'9':'09'
})
df = df.merge(exp19[['Total soins de ville','Département']], how='left', on='Département').dropna() # DOM
df.head()
C:\Users\nicco\anaconda3\envs\test-env\lib\site-packages\ipykernel_launcher.py:5: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
| Département | Nom du département | Total ALD | Insuffisance cardiaque grave, troubles du rythme graves, cardiopathies valvulaires graves, cardiopathies congénitales graves (ALD5) | Diabète de type 1 et diabète de type 2 (ALD8) | Maladie coronaire (ALD13) | Affections psychiatriques de longue durée (ALD23) | Tumeur maligne, affection maligne du tissu lymphatique ou hématopoïétique (ALD30) | Ensemble_0 à 19 ans | Ensemble_20 à 39 ans | ... | Hommes_60 à 74 ans | Hommes_75 ans et plus | Hommes_Total | Femmes_0 à 19 ans | Femmes_20 à 39 ans | Femmes_40 à 59 ans | Femmes_60 à 74 ans | Femmes_75 ans et plus | Femmes_Total | Total soins de ville | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 01 | Ain | 20643 | 1963 | 4565 | 1994 | 2122 | 3873 | 168091 | 151167 | ... | 49114 | 21362 | 321823 | 81217 | 75735 | 89407 | 52928 | 31750 | 331037 | 5.106213e+08 |
| 1 | 02 | Aisne | 23514 | 2199 | 6284 | 2417 | 2160 | 3718 | 132910 | 115437 | ... | 46110 | 18188 | 258739 | 64489 | 57605 | 69533 | 50124 | 30495 | 272246 | 4.581757e+08 |
| 2 | 03 | Allier | 25140 | 2151 | 5169 | 2370 | 3503 | 4353 | 68963 | 62009 | ... | 33500 | 17570 | 160818 | 33358 | 30707 | 44301 | 38055 | 28298 | 174719 | 2.888785e+08 |
| 3 | 04 | Alpes-de-Haute-Provence | 23538 | 2065 | 4135 | 2143 | 3540 | 3887 | 34398 | 30454 | ... | 16909 | 8522 | 80022 | 16330 | 15283 | 22619 | 17991 | 12418 | 84641 | 1.667391e+08 |
| 4 | 05 | Hautes-Alpes | 23613 | 2367 | 3490 | 1941 | 3462 | 3945 | 30038 | 27524 | ... | 13947 | 6382 | 68591 | 14399 | 13705 | 19333 | 14966 | 9563 | 71966 | 1.269668e+08 |
5 rows × 27 columns
y = df['Total ALD']
X = df.drop(columns=['Total ALD','Département','Nom du département'], axis=1)
from xgboost import XGBRegressor, XGBClassifier, plot_tree
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error, accuracy_score, confusion_matrix
model = XGBRegressor()
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.10, random_state=42, shuffle=True
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test,y_pred)
mape = mean_absolute_percentage_error(y_test,y_pred)
print('mae:',mae)
print('mape:',mape*100)
mae: 593.5298828125 mape: 2.7662334721251076
Conclusion: Precis mais ne necessite que "Insuffisance cardiaque grave, troubles du rythme graves, cardiopathies valvulaires graves, cardiopathies congénitales graves (ALD5)" pour prendre une decision, cela n'ait pas utile pour expliquer Hétérogénéité des depenses en soins de ville
def categorise(x):
if x<0.5e9: # 0.5B €
return 'Moins de 0.5 milliards'
elif x>0.5e9 and x<1e9:
return 'Entre 0.5 et 1 milliards'
elif x>1e9 and x<1.5e9:
return 'Entre 1 et 1.5 milliards'
elif x>1.5e9:
return 'Plus de 1.5 milliards'
df['categorie de dépense'] = df['Total soins de ville'].apply(categorise)
df['categorie de dépense'].value_counts()
Moins de 0.5 milliards 55 Entre 0.5 et 1 milliards 20 Entre 1 et 1.5 milliards 16 Plus de 1.5 milliards 5 Name: categorie de dépense, dtype: int64
y = df['categorie de dépense']
X = df.drop(columns=['categorie de dépense','Département','Nom du département', 'Total soins de ville'], axis=1)
model = XGBClassifier()
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, random_state=42, shuffle=True
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test,y_pred)
print('accuracy:',acc) # 85 %
print(confusion_matrix(y_test,y_pred))
C:\Users\nicco\anaconda3\envs\test-env\lib\site-packages\xgboost\sklearn.py:1146: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
[10:35:32] WARNING: C:/Users/Administrator/workspace/xgboost-win64_release_1.4.0/src/learner.cc:1095: Starting in XGBoost 1.3.0, the default evaluation metric used with the objective 'multi:softprob' was changed from 'merror' to 'mlogloss'. Explicitly set eval_metric if you'd like to restore the old behavior. accuracy: 0.85 [[4 0 1 0] [0 3 0 0] [1 0 8 0] [0 1 0 2]]
import matplotlib.pyplot as plt
plt.figure()
plot_tree(model)
plt.savefig('tree.svg', format='svg', dpi=1200)
<Figure size 432x288 with 0 Axes>
Conclusion: Bien meilleurs resultats en utilisant le total des soins de ville en tant que variable dependendante et en construisant des categories de depenses de facon telle a avoir assez d'observables dans chacune des categories (choisies grace au violin plot) afin de pouvoir entrainer un algorithme d'apprentissage statistique sur un probleme de classification. Avec un training set tres reduit de seulement 76 observables et un test set de 26 observabables le modele arrive a predire correctement la categorie de depense avec une accuracy de 85 %. Plus precisesement, etant les quatre classes de depenses on remarque, en s'appuyant sur la matrice de confusion, que seules 3 observables donnent des predictions fausses. De plus, la matrice de confusion nous montre aussi que des examples pour chaque classe apparaissent dans le test set, ce qui nous indique que l'algorithme peut s'appuyer sur toutes les regles apprisent pour faire son choix.
Quatre regles emergeant de l'algorithme sont les suivantes:
Afin d'expliquer l'hétérogénéité des depenses en soins de ville entre les département il convient maintenant de verifier dans quelles proportions ses regles s'appliquent sur l'ensemble des donnees
# Apply rules from decision tree
df1 = df[df['Hommes_75 ans et plus'] < 23127.5] # 52 rows - Majority "Moins de 0.5 milliards"
df2 = df[(df['Hommes_75 ans et plus'] > 23127.5) & (df['Femmes_40 à 59 ans'] > 143373.5)] # 21 rows - HIgh level of expenses, Entre 1 et 1.5 milliards (16) Plus de 1.5 milliard 5
df3 = df[(df['Hommes_75 ans et plus'] > 23127.5) &
(df['Femmes_40 à 59 ans'] < 143373.5) &
(df['Ensemble_40 à 59 ans'] < 160918.5) # 6 rows - low expenses spectrum
]
df4 = df[(df['Hommes_75 ans et plus'] > 23127.5) &
(df['Femmes_40 à 59 ans'] < 143373.5) &
(df['Ensemble_40 à 59 ans'] > 160918.5) # 17 rows - Majority "Entre 0.5 et 1 milliards"
]
fig = px.scatter(
df,
x = 'Hommes_75 ans et plus',
y= 'Femmes_40 à 59 ans',
color = 'categorie de dépense',
size = 'Ensemble_40 à 59 ans',
width=1000, height=700,
template="simple_white",
title = 'Distribution des départements par catégorie de dépense en fonction de la séniorité des individus',
category_orders={'categorie de dépense':[
'Moins de 0.5 milliards', 'Entre 0.5 et 1 milliards',
'Entre 1 et 1.5 milliards', 'Plus de 1.5 milliards'
]},
labels = {
'Hommes_75 ans et plus':'Hommes de plus de 75 ans',
'Femmes_40 à 59 ans':'Femmes de 40 à 59 ans'
}
)
fig.update_yaxes(
showgrid=True
)
fig.add_shape( # add a vartical "target" line
type="line", line_color="salmon", line_width=3, opacity=0.5, line_dash="dot",
x0=23127.5, x1=23127.5, xref="x", y0=0, y1=350e3, yref="y"
)
fig.add_shape( # add a vartical "target" line
type="line", line_color="salmon", line_width=3, opacity=0.5, line_dash="dot",
x0=0, x1=80e3, xref="x", y0=143373.5, y1=143373.5, yref="y"
)
fig.add_annotation( # Text
text="23k Hommes de 75 ans et plus", x=23127.5, y=330e3, arrowhead=1, showarrow=False
)
fig.add_annotation( # Text
text="140k Femmes de 40 a 59 ans", x=80e3, y=150000, arrowhead=1, showarrow=False
)